package inabit.compiler.parser.grammar;

import inabit.compiler.lexical.TokenClass;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**Represents a context-free grammar*/
/**@author Rafael Rocha*/
public class Grammar {
	private Map<TokenClass, Terminal> terminals;
	private List<Production> productionRules;
	private NonTerminal initialSymbol;
	private TokenClass eof;
	
	public Grammar() {
		this.terminals = new HashMap<TokenClass, Terminal>();
		this.productionRules = new ArrayList<Production>();
		this.initialSymbol = null;
	}
	
	/**Chooses one grammar symbol to be the initial variable symbol, if a initial symbol was already
	 * chosen then it is replaced by the new one, if the symbol is not in grammar it throws an InvalidParameterException*/
	public void setInitialSymbol(String nonTerminalLabel) throws InvalidParameterException{
		this.initialSymbol = new NonTerminal(nonTerminalLabel);
	}
	
	public void addTerminal(Terminal terminal) {
		this.terminals.put(terminal.getTokenClass(), terminal);
	}
	public void addTerminals(Terminal... terminals){
		for(Terminal terminal : terminals) {
			addTerminal(terminal);
		}
	}
	public void addTerminals(TokenClass... tokenClasses) {
		for(TokenClass tkClasses : tokenClasses ){
			addTerminal(new Terminal(tkClasses));
		}
	}
	public void addTerminals(Collection<TokenClass> tokenClasses) {
		for(TokenClass tkClasses : tokenClasses ){
			addTerminal(new Terminal(tkClasses));
		}
	}
	
	public void addProductionRule(Production rule) {
		this.productionRules.add(rule);
	}
	public void addProductionRules(Production... rules) {
		for(Production rule : rules) {
			this.productionRules.add(rule);
		}
	}
	public void addProductionRules(Collection<Production> rules) {
		for(Production rule : rules) {
			this.productionRules.add(rule);
		}
	}

	public NonTerminal getInitialSymbol() {
		return this.initialSymbol;
	}
	public void setEOF(TokenClass eof) {
		this.eof = eof;
	}
	public TokenClass eof(){
		return eof;
	}
	
	public Collection<Terminal> getTerminals() {
		return this.terminals.values();
	}
	
	public List<Production> getProductionRules() {
		return productionRules;
	}
}
